#pragma once
#include <queue>
#include <functional>
#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>

class TaskQueue
{
public:
	typedef std::function<void()> Task;
	void Queue(const Task& task)
	{
		boost::lock_guard<boost::mutex> lock(m_mutex);
		if (!m_thread.joinable())
			m_thread = boost::thread(&TaskQueue::WorkerThread, this);
		m_tasks.push(task);
		m_cond.notify_all();
	}
	void Join()
	{
		m_thread.interrupt();
		if (m_thread.joinable())
			m_thread.join();
	}
private:
	void WorkerThread()
	{
		try
		{
			for (;;)
			{
				std::queue<Task> tasks;
				{
					boost::unique_lock<boost::mutex> lock(m_mutex);
					if (m_tasks.empty())
						m_cond.wait(lock);
					m_tasks.swap(tasks);
				}
				while (!tasks.empty())
				{
					const Task& task = tasks.front();
					task();
					tasks.pop();
					boost::this_thread::interruption_point();
				}
			}
		}
		catch (const boost::thread_interrupted&)
		{
		}
	}
	std::queue<Task> m_tasks;
	boost::mutex m_mutex;
	boost::condition_variable m_cond;
	boost::thread m_thread;
};